package com.gitee.cirnochat.websocket.interceptor;


import com.gitee.cirnochat.chat.model.enums.MessageOperation;
import com.gitee.cirnochat.chat.model.enums.MessageResponseStatus;
import com.gitee.cirnochat.common.exception.BusinessException;
import com.gitee.cirnochat.user.service.UserService;
import com.gitee.cirnochat.websocket.model.vo.resp.WebSocketMessageResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.stereotype.Component;

import java.security.Principal;
import java.util.Map;
import java.util.Objects;

import static com.gitee.cirnochat.common.constant.RedisKey.*;
import static com.gitee.cirnochat.common.constant.WebSocketConstant.*;

/**
 * WebSocket 通道拦截器
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class MyChannelInterceptor implements ChannelInterceptor {

    private final UserService userService;
    private final StringRedisTemplate redisTemplate;
    private final ObjectProvider<SimpMessagingTemplate> messagingTemplateProvider;
    private final static String SUBSCRIBE_PREFIX = "/user";

    private void sendErrorMessageToUser(String userId, MessageResponseStatus status) {
        if (StringUtils.isBlank(userId)) {
            return;
        }
        messagingTemplateProvider.ifAvailable((consumer) -> {
            consumer.convertAndSendToUser(userId,
                    ERRORS_SUBSCRIBE_CHANNEL,
                    WebSocketMessageResponse.Builder
                            .code(status.getValue())
                            .message(status.getDescription())
                            .operation(MessageOperation.NOTIFY)
                            .build()
            );
        });
    }

    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel) {
        StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
        if (accessor == null || accessor.getCommand() == null) {
            return message;
        }

        try {
            StompCommand command = accessor.getCommand();
            switch (command) {
                case StompCommand.CONNECT -> {
                    String token = accessor.getFirstNativeHeader("Authorization");
                    String deviceId = accessor.getFirstNativeHeader("X-Device-Id");
                    String deviceName = accessor.getFirstNativeHeader("X-Device-Name");
                    String deviceType = accessor.getFirstNativeHeader("X-Device-Type");
                    if (StringUtils.isAnyBlank(token, deviceId, deviceName, deviceType)) {
                        log.warn("[{}] 连接请求中缺少必要的头信息", command);
                        return null;
                    }
                    Long userId = userService.validateAndParseUserId(token, deviceId);
                    accessor.setUser(() -> String.valueOf(userId));
                    Map<String, Object> sessionAttributes = accessor.getSessionAttributes();
                    if (sessionAttributes != null) {
                        sessionAttributes.put("deviceId", deviceId);
                        sessionAttributes.put("deviceName", deviceName);
                        sessionAttributes.put("deviceType", deviceType);
                    }
                }
                case StompCommand.SUBSCRIBE -> {
                    String destination = accessor.getDestination();
                    if (destination == null || destination.isBlank()) {
                        log.warn("[{}] 订阅地址为空", command);
                        return null;
                    }
                    Principal user = accessor.getUser();
                    if (user == null) {
                        return null;
                    }
                    long userId = Long.parseLong(user.getName());
                    log.info("用户订阅的频道: {}", destination);
                    // 设置聊天频道的订阅状态,防止消息丢失，仅当用户订阅时发送
                    switch (destination) {
                        case SUBSCRIBE_PREFIX + CHAT_SUBSCRIBE_CHANNEL -> {
                            Objects.requireNonNull(accessor.getSessionAttributes()).put(accessor.getSubscriptionId(), destination);
                            redisTemplate.opsForValue().set(USER_CHAT_CHANNEL_SUBSCRIBE_STATE_KEY + userId, "1");
                        }
                        case SUBSCRIBE_PREFIX + SESSION_STATUS_SUBSCRIBE_CHANNEL -> {
                            final String sessionId = accessor.getFirstNativeHeader("sessionId");
                            if (sessionId == null) {
                                log.warn("心跳订阅请求中未提供 sessionId");
                                return null;
                            }
                            redisTemplate.opsForValue().set(USER_SESSION_CHANNEL_SUBSCRIBE_STATE_KEY + userId, sessionId);
                            Objects.requireNonNull(accessor.getSessionAttributes()).put(accessor.getSubscriptionId(), destination);
                        }
                        case SUBSCRIBE_PREFIX + WEBRTC_SUBSCRIBE_CHANNEL -> {
                            Objects.requireNonNull(accessor.getSessionAttributes()).put(accessor.getSubscriptionId(), destination);
                            redisTemplate.opsForValue().set(USER_SUBSCRIBE_CALLING_CHANNEL + userId, "1");
                        }
                    }
                }
                case StompCommand.UNSUBSCRIBE -> {
                    Principal user = accessor.getUser();
                    if (user == null) {
                        return null;
                    }
                    long userId = Long.parseLong(user.getName());
                    log.info("用户取消订阅的频道: {}", accessor.getDestination());
                    String subscribeChannel = (String) accessor.getSessionAttributes().get(accessor.getSubscriptionId());
                    if (subscribeChannel != null) {
                        // 设置聊天频道的订阅状态,防止消息丢失，仅当用户订阅时发送
                        switch (subscribeChannel) {
                            case SUBSCRIBE_PREFIX + CHAT_SUBSCRIBE_CHANNEL -> redisTemplate.delete(USER_CHAT_CHANNEL_SUBSCRIBE_STATE_KEY + userId);
                            case SUBSCRIBE_PREFIX + SESSION_STATUS_SUBSCRIBE_CHANNEL -> redisTemplate.delete(USER_SESSION_CHANNEL_SUBSCRIBE_STATE_KEY + userId);
                            case SUBSCRIBE_PREFIX + WEBRTC_SUBSCRIBE_CHANNEL -> redisTemplate.delete(USER_SUBSCRIBE_CALLING_CHANNEL + userId);
                        }
                    }
                }
                case StompCommand.DISCONNECT -> {
                    log.info("用户 {} 断开连接 WebSocket", accessor.getUser() != null ? accessor.getUser().getName() : "未知用户");
                }
            }
        } catch (BusinessException ex) {
            Principal user = accessor.getUser();
            if (user == null) {
                return null;
            }
            long userId = Long.parseLong(user.getName());
            log.error("STOMP 鉴权过程异常: {}", ex.getMessage(), ex);
            sendErrorMessageToUser(String.valueOf(userId), MessageResponseStatus.TOKEN_EXPIRED);
            return null;
        }

        return message;
    }

}
